package com.micro.modual.service.impl;

import com.micro.common.MicroConstant;
import com.micro.common.MicroResponseCode;
import com.micro.common.biz.GeneratorCodeUtil;
import com.micro.common.biz.TokenCacheKeyUtil;
import com.micro.common.util.JacksonUtil;
import com.micro.common.util.ObjectUtil;
import com.micro.core.exception.BizException;
import com.micro.core.handler.BaseContextHandler;
import com.micro.core.resp.RespJson;
import com.micro.core.resp.RespJsonData;
import com.micro.modual.async.completion.manager.CompletionExecuteManager;
import com.micro.modual.entity.LoginAuth;
import com.micro.modual.entity.SystemUser;
import com.micro.modual.entity.User;
import com.micro.modual.extension.LoginAuthExtension;
import com.micro.modual.extension.UserExtension;
import com.micro.modual.mapper.LoginAuthMapper;
import com.micro.modual.mapper.SystemUserMapper;
import com.micro.modual.mapper.UserMapper;
import com.micro.modual.model.bo.login.BindUserPhoneBO;
import com.micro.modual.model.bo.login.LoginBO;
import com.micro.modual.model.dto.token.TokenDTO;
import com.micro.modual.model.vo.GeneratorTokenInfo;
import com.micro.modual.service.LoginService;
import com.micro.plugins.jwt.base.JWTInfo;
import com.micro.plugins.jwt.base.JWTInfoInterface;
import com.micro.plugins.jwt.configuration.JwtAuthConfiguration;
import com.micro.plugins.jwt.load.JwtLoadConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.omg.CORBA.UserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * <h1>业务实现层</h1>
 * @author LevelCoder
 * @version V1.0.0
 * @email LevelCoder@126.com
 * @date 2020/5/1
 */
@Slf4j
@Service("loginService")
public class LoginServiceImpl implements LoginService {

    private static final String ACCESS_TOKEN = "accessToken";

    private static final String REFRESH_TOKEN = "refreshToken";

    @Autowired
    private JwtAuthConfiguration jwtAuthConfiguration;

    @Autowired
    private JwtLoadConfiguration jwtLoadConfiguration;

    @Autowired
    private LoginAuthMapper loginAuthMapper;

    @Autowired
    private SystemUserMapper systemUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CompletionExecuteManager completionExecuteManager;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * <h2>登陆操作 获取token信息</h2>
     * @param loginBO {@link LoginBO} 登录请求对象
     * @param request  {@link HttpServletRequest} 用于在生成token时候获取请求头信息,区分请求客户端
     * @return {@link TokenDTO}  登录请求返回对象
     * @throws Exception
     */
    @Override
    public RespJsonData<TokenDTO> createAuthenticationToken(LoginBO loginBO, HttpServletRequest request) throws Exception {
        switch (loginBO.getLogingType()){
            case MicroConstant.LoginType.ORDINARY_LOGIN:
                return ordinaryLogin(loginBO,request);
            case MicroConstant.LoginType.QUICK_LOGIN:
                return quickLogin(loginBO,request);
            case MicroConstant.LoginType.OPEN_LOGIN:
                return openLogin(loginBO,request);
            default:
                throw new BizException(MicroResponseCode.LOGIN_ORIGIN_ERROR.getResponseCode(),MicroResponseCode.LOGIN_ORIGIN_ERROR.getResponseMessage());
        }
    }

    /**
     * <h2>第三方登录</h2>
     * @param loginBO  {@link LoginBO} 登录请求对象
     * @param request   http请求对象
     * @return
     */
    private RespJsonData<TokenDTO> openLogin(LoginBO loginBO, HttpServletRequest request) {
        log.info("第三方登录开始***********************************openLogin > loginBO:{}", JacksonUtil.toJson(loginBO));
        switch (loginBO.getThirdType()){
            case MicroConstant.AuthType.AUTH_WECHAT:
                //微信登录
                log.info("微信登录开始***********************************openLogin > loginBO:{}",JacksonUtil.toJson(loginBO));
                return weChatLogin(loginBO,request);
            default:
                return new RespJsonData(null,MicroResponseCode.LOGIN_FAIL.getResponseCode(),MicroResponseCode.LOGIN_FAIL.getResponseMessage());
        }
    }

    /**
     * <h2>第三方登录微信登录</h2>
     * @param loginBO  {@link LoginBO} 登录请求对象
     * @param request   http请求对象
     * @return
     */
    private RespJsonData<TokenDTO> weChatLogin(LoginBO loginBO, HttpServletRequest request) {
        return null;
    }

    /**
     * <h2>手机号快捷登录</h2>
     * @param loginBO  {@link LoginBO} 登录请求对象
     * @param request  http请求对象
     * @return
     */
    private RespJsonData<TokenDTO> quickLogin(LoginBO loginBO, HttpServletRequest request) throws Exception {
        log.info("手机快捷登录开始***********************************quickLogin > loginBO:{}", JacksonUtil.toJson(loginBO));
//        ValidateHelper.validateCode(loginBO.getLoginName(),MessageTypeEnum.LOGIN.getMessageType(),loginBO.getCertificate());
        //校验是否存在用户
        switch (loginBO.getUserType()){
            case MicroConstant.CustomerType.MEMBER_USER_TYPE:
                return memberPhoneLogin(loginBO,request);
            default:
                throw new BizException(MicroResponseCode.CUSTOMER_TYPE_ERROR.getResponseCode(),MicroResponseCode.CUSTOMER_TYPE_ERROR.getResponseMessage());
        }
    }

    /**
     * 普通用户手机号登录校验
     * @param loginBO
     * @param request
     * @return
     */
    private RespJsonData<TokenDTO> memberPhoneLogin(LoginBO loginBO, HttpServletRequest request) throws Exception {
        LoginAuth loginAuth = loginAuthMapper.findLoginAuthInfo(loginBO.getLoginName(),loginBO.getUserType());
        if(ObjectUtil.isNotEmpty(loginAuth) && ObjectUtil.isNotEmpty(loginAuth.getCustomerCode())){
            return validateAndGeneratorToken(loginBO,loginAuth,request);
        }else {
            String userCode = GeneratorCodeUtil.generatorCustomerCode();
            User user = UserExtension.initUserForPhoneLogin(loginBO.getLoginName(),userCode);
            userMapper.insertSelective(user);
            LoginAuth la = LoginAuthExtension.createLoginAuth(userCode,loginBO);
            loginAuthMapper.insertSelective(la);
            completionExecuteManager.computeRule(MicroConstant.AsyncRuleTypeCode.ASYNC_DEFAULT_USER_INFO,userCode);
            GeneratorTokenInfo generatorTokenInfo = new GeneratorTokenInfo();
            generatorTokenInfo.setCustomerCode(userCode);
            generatorTokenInfo.setCustomerType(loginBO.getUserType());
            generatorTokenInfo.setCustomerName(loginBO.getLoginName());
            //验证通过生成token返回给客户端
            return generatorToken(generatorTokenInfo,request);
        }
    }




    /**
     * <h2>账号密码登陆</h2>
     * @param loginBO   {@link LoginBO} 登录请求对象
     * @param request   http请求对象
     * @return
     */
    private RespJsonData<TokenDTO> ordinaryLogin(LoginBO loginBO, HttpServletRequest request) throws Exception {
        LoginAuth loginAuth = loginAuthMapper.findLoginAuthInfo(loginBO.getLoginName(),loginBO.getUserType());
        if(ObjectUtil.isNotEmpty(loginAuth) && ObjectUtil.isNotEmpty(loginAuth.getCustomerCode())){
            return validateAndGeneratorToken(loginBO,loginAuth,request);
        }else {
            throw new BizException( MicroResponseCode.USER_NOT_EXIST.getResponseCode(),MicroResponseCode.USER_NOT_EXIST.getResponseMessage());
        }
    }

    /**
     * <h2>验证账户有效性和生成登录成功凭证</h2>
     * @param loginAuth
     * @return
     */
    private RespJsonData<TokenDTO> validateAndGeneratorToken(LoginBO loginBO,LoginAuth loginAuth,HttpServletRequest request) throws Exception {
        GeneratorTokenInfo generatorTokenInfo = findGeneratorTokenInfo(loginAuth);
        if(ObjectUtil.isNotEmpty(generatorTokenInfo)){
            //验证用户登陆是否被锁定
            if(generatorTokenInfo.getLockStatus().equals(MicroConstant.StatusEffective.LOCKED)){
                throw new BizException(MicroResponseCode.USER_HAS_LOCKED.getResponseCode(),MicroResponseCode.USER_HAS_LOCKED.getResponseMessage());
            }
            //验证账户密码是否正确
            if(loginBO.getLogingType().equals(MicroConstant.LoginType.ORDINARY_LOGIN)){
                String waitPassWord = DigestUtils.md5DigestAsHex((DigestUtils.md5DigestAsHex(loginBO.getCertificate().getBytes())+ generatorTokenInfo.getCustomerLoginPassWordSalt()).getBytes());
                if(!loginAuth.getCredential().equals(waitPassWord)){
                    throw new BizException(MicroResponseCode.USER_HAS_WRONG_WITH_ACCOUNT_OR_PASSWORD.getResponseCode(),MicroResponseCode.USER_HAS_WRONG_WITH_ACCOUNT_OR_PASSWORD.getResponseMessage());
                }
            }
            //验证账户信息有效性
            if(generatorTokenInfo.getCustomerStatus().equals(MicroConstant.StatusEffective.DISABLE)){
                throw new BizException(MicroResponseCode.USER_HAS_FORBIDDEN.getResponseCode(),MicroResponseCode.USER_HAS_FORBIDDEN.getResponseMessage());
            }
            //验证通过生成token返回给客户端
            return generatorToken(generatorTokenInfo,request);
        }else {
            throw new BizException( MicroResponseCode.USER_NOT_EXIST.getResponseCode(),MicroResponseCode.USER_NOT_EXIST.getResponseMessage());
        }
    }

    /**
     * <h1>拼接生成token对象参数,包含用户校验信息</h1>
     * @param loginAuth {@link LoginAuth} 用户登录授权表表
     * @return
     */
    private GeneratorTokenInfo findGeneratorTokenInfo(LoginAuth loginAuth) {
        GeneratorTokenInfo generatorTokenInfo = new GeneratorTokenInfo();
        if(loginAuth.getCustomerType().equals(MicroConstant.CustomerType.SYSTEM_USER_TYPE)){
            SystemUser systemUser = systemUserMapper.findUserInfoByUserCode(loginAuth.getCustomerCode());
            if(ObjectUtil.isNotEmpty(systemUser)){
                generatorTokenInfo.setCustomerCode(systemUser.getUserCode());
                generatorTokenInfo.setCustomerName(systemUser.getUserNickName());
                generatorTokenInfo.setCustomerLoginPassWordSalt(systemUser.getPasswordSalt());
                generatorTokenInfo.setCustomerType(loginAuth.getCustomerType());
                generatorTokenInfo.setCustomerStatus(systemUser.getUserStatus());
                generatorTokenInfo.setLockStatus(systemUser.getPasswordStatus());
            }else {
                throw new BizException(MicroResponseCode.USER_NOT_EXIST.getResponseCode(),MicroResponseCode.USER_NOT_EXIST.getResponseMessage());
            }
        }else if(loginAuth.getCustomerType().equals(MicroConstant.CustomerType.MEMBER_USER_TYPE)){
            User user = userMapper.findUserInfoByUserCode(loginAuth.getCustomerCode());
            if(ObjectUtil.isNotEmpty(user)){
                generatorTokenInfo.setCustomerCode(user.getUserCode());
                generatorTokenInfo.setCustomerName(user.getUserNickName());
                generatorTokenInfo.setCustomerLoginPassWordSalt(user.getPasswordSalt());
                generatorTokenInfo.setCustomerType(loginAuth.getCustomerType());
                generatorTokenInfo.setCustomerStatus(user.getUserStatus());
                generatorTokenInfo.setLockStatus(user.getPasswordStatus());
            }else {
                throw new BizException(MicroResponseCode.USER_NOT_EXIST.getResponseCode(),MicroResponseCode.USER_NOT_EXIST.getResponseMessage());
            }
        }
        return generatorTokenInfo;
    }

    /**
     * <h2>验证通过生成token返回给客户端</h2>
     * @param generatorTokenInfo       生成token信息
     * @param request                  http请求对象
     * @return
     */
    private RespJsonData<TokenDTO> generatorToken( GeneratorTokenInfo generatorTokenInfo, HttpServletRequest request) throws Exception {
        String channelType = request.getHeader(jwtLoadConfiguration.getMicroJwt().getChannelHeader());
        String version = request.getHeader(jwtLoadConfiguration.getMicroJwt().getVersionHeader());
        String clientType = jwtAuthConfiguration.findClientType(channelType);
        JWTInfo jwtInfo = new JWTInfo(generatorTokenInfo.getCustomerName(),generatorTokenInfo.getCustomerCode(),generatorTokenInfo.getCustomerType(),generatorTokenInfo.getCustomerName(),version, channelType);
        String accessToken = jwtAuthConfiguration.generateToken(jwtInfo,ACCESS_TOKEN,clientType);
        String refreshToken = jwtAuthConfiguration.generateToken(jwtInfo,REFRESH_TOKEN,clientType);
        TokenDTO tokenDTO = new TokenDTO(accessToken,refreshToken);
        return new RespJsonData<TokenDTO>(tokenDTO,MicroResponseCode.SUCCESS.getResponseCode(), MicroResponseCode.SUCCESS.getResponseMessage());
    }

    /**
     * <h2>刷新token</h2>
     * @param customerCode  客户编号
     * @param request       {@link HttpServletRequest} 用于在生成token时候获取请求头信息,区分请求客户端
     * @return  {@link TokenDTO}  登录请求返回对象
     * @throws Exception
     */
    @Override
    public RespJsonData<TokenDTO> refreshToken(String customerCode, HttpServletRequest request) throws Exception {
        log.info("******************************************开始进行刷新TOKEN******************************");
        String refreshToken = request.getHeader(jwtLoadConfiguration.getMicroJwt().getRefreshTokenHeader());
        String channelType = request.getHeader(jwtLoadConfiguration.getMicroJwt().getChannelHeader());
        String clientType = jwtAuthConfiguration.findClientType(channelType);
        log.info("请求参数 >> customerCode:{},refreshToken:{}",customerCode,refreshToken);
        log.info("是否存在缓存中 >> key:{},result:{}",TokenCacheKeyUtil.getRefreshTokenKey(customerCode,clientType),redisTemplate.hasKey(TokenCacheKeyUtil.getRefreshTokenKey(customerCode,clientType)));
        if(ObjectUtil.isEmpty(customerCode)){
            //校验token有效性
            if(jwtAuthConfiguration.validataToken(refreshToken)){
                //如果token存在 则解析token 获取客户编号
                customerCode = jwtAuthConfiguration.getInfoFromToken(refreshToken).getCustomerCode();
            }
        }
        if(redisTemplate.hasKey(TokenCacheKeyUtil.getRefreshTokenKey(customerCode,clientType))){
            log.info("缓存中所存在的Key >> cacheKey:{}",redisTemplate.opsForValue().get(TokenCacheKeyUtil.getRefreshTokenKey(customerCode,clientType)).equals(refreshToken));
            log.info("请求头所携带的Key >> requestKey:{}",refreshToken);
            if(redisTemplate.opsForValue().get(TokenCacheKeyUtil.getRefreshTokenKey(customerCode,clientType)).equals(refreshToken)){
                JWTInfoInterface jwtInfoInterface = jwtAuthConfiguration.getInfoFromToken(refreshToken);
                String newAccessToken = jwtAuthConfiguration.generateToken(jwtInfoInterface,ACCESS_TOKEN,clientType);
                String newRefreshToken = jwtAuthConfiguration.generateToken(jwtInfoInterface,REFRESH_TOKEN,clientType);
                TokenDTO tokenDTO = new TokenDTO(newAccessToken,newRefreshToken);
                log.info("************************************刷新成功后更新缓存*********************************");
                log.info("返回请求token  >> newAccessToken:{}",newAccessToken);
                log.info("返回刷新token  >> newRefreshToken:{}",newRefreshToken);

                return new RespJsonData(tokenDTO,MicroResponseCode.SUCCESS.getResponseCode(),MicroResponseCode.SUCCESS.getResponseMessage());
            }else{
                return new RespJsonData(null,MicroResponseCode.AUTH_ACCOUNT_LANDING_ON_OTHER_DEVICES.getResponseCode(),MicroResponseCode.AUTH_ACCOUNT_LANDING_ON_OTHER_DEVICES.getResponseMessage());
            }
        }else{
            return new RespJsonData(null,MicroResponseCode.AUTH_TOKEN_INVALIDATION.getResponseCode(),MicroResponseCode.AUTH_TOKEN_INVALIDATION.getResponseMessage());
        }
    }

    /**
     * <h2>账户退出</h2>
     * @return {@link RespJson} 返回结果基础类
     */
    @Override
    public RespJson logout(HttpServletRequest request) {
        String channelType = request.getHeader(jwtLoadConfiguration.getMicroJwt().getChannelHeader());
        String clientType = jwtAuthConfiguration.findClientType(channelType);
        redisTemplate.delete(TokenCacheKeyUtil.getRefreshTokenKey(BaseContextHandler.getCustomerCode(),clientType));
        redisTemplate.delete(TokenCacheKeyUtil.getAccressTokenKey(BaseContextHandler.getCustomerCode(),clientType));
        return new RespJson(MicroResponseCode.SUCCESS.getResponseCode(),MicroResponseCode.SUCCESS.getResponseMessage());
    }

    @Override
    public RespJsonData<TokenDTO> modifyOpenLoginBindUserPhone(BindUserPhoneBO bindUserPhoneBO) {
        return null;
    }

    /**
     * <h2>校验token有效性</h2>
     * @param token 请求token
     * @return
     */
    @Override
    public RespJson validataRequestToken(String token) throws Exception {
        try {
            JWTInfoInterface jwtInfo = jwtAuthConfiguration.getInfoFromToken(token);
            if(ObjectUtil.isNotEmpty(jwtInfo)){
                return new RespJson(MicroResponseCode.SUCCESS.getResponseCode(),MicroResponseCode.SUCCESS.getResponseMessage());
            }else {
                return new RespJson(MicroResponseCode.FAIL.getResponseCode(),MicroResponseCode.FAIL.getResponseMessage());
            }

        } catch (BizException e) {
            e.printStackTrace();
            return new RespJson(e.getResponseCode(),e.getResponseMessage());
        }


    }
}
